/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package MiddleLayer;

import GUI.*;
import GUI.Hearts.GameState;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;

/**
 *
 * @author Green
 */
public class ClientServerMessenger {

    Socket socketClient;//socket kết nối với server
    ClientReceiver receiver;//cái này là tiểu trình lắng nghe server
    Hearts game;//game của mình.
    GameRules gameRules;
    JPanel pnlInfo;//panel chứa control chat và hiển thị thông tin
    boolean exit = false;//cái này là cờ để thoát
    boolean commandfinish = false;
    GUICard played;

    public ClientServerMessenger() {
        receiver = new ClientReceiver();
        receiver.start();//bắt đầu tiểu trình lắng nghe server
    }

    //info là panel chứa thông tin, chat
    //game là cái game mình đang chạy
    public void setup(JPanel info, Hearts game) {
        this.game = game;
        this.pnlInfo = info;

        this.gameRules = new GameRules();
    }

    //gửi thông tin đến server
    public void sendToServer(String name, int guiCardID) {
        System.out.println(name + " % " + guiCardID);
        this.game.setPlayerState(Hearts.PlayerState.DoNothing);

        this.played = this.client.findCardInAvailList(guiCardID % 13, guiCardID / 13);
        this.finishTask(GameState.Playing);
        this.commandfinish = true;
    }

    //gửi thông tin chat đến server
    public void sendChatMessage(String m) {
        System.out.println(m);
    }

    public void finishTask(GameState state) {
        System.out.println(state.toString() + " completed ");
        this.commandfinish = true;
    }

    public boolean checkRules(int nextguicardid, int firstcardonboard, Cards playersCards) {
        int id = Card.convertCardGUI_IDToCard_ID(nextguicardid);
        int fisrt = Card.convertCardGUI_IDToCard_ID(firstcardonboard);
        return this.gameRules.isMovingValid(nextguicardid, fisrt, playersCards);
    }

    public boolean checkRules_FirstTurn(int nextguicardid) {
        int id = Card.convertCardGUI_IDToCard_ID(nextguicardid);
        return this.gameRules.isLeadMovingValid(id);
    }

    //Lớp chuyên lắng nghe server
    private class ClientReceiver extends Thread {

        @Override
        public void run() {
            boolean start = false;
            int i = 0;
            while (!exit) {
                //cái này tui đang dùng để test
                System.out.println("i=" + i + "Command : ");
                String command = readInputFromConsole();
                if (command.compareTo(".") == 0) {
                    start = true;
                    commandfinish = true;
                    testInitClients();
                }
                if (command.compareToIgnoreCase("exit") == 0) {
                    exit = true;
                }
                if (start) {
                    //processCommand(command);
                    if (commandfinish) {
                        runTest(i);
                        i++;
                    }
                }
            }
        }

        private String readInputFromConsole() {
            String s = "";
            try {
                BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));
                s = bufferRead.readLine();

                System.out.println(s);
            } catch (IOException e) {
                System.err.println(e);
            }

            return s;
        }
    }

    private class MethodResult {

        Method method;
        Object obj;
    }
    //Phần dưới là để test
    private GamblerManager manager;
    private Gambler client;
    private String clientHas2OfClub;

    private void processCommand(String command) {
        String methodName = "";
//                methodName = "testInitClients";
//                MethodResult result = getMethodByName(methodName);
//                result.method.invoke(result.obj, new Object[]{});

        if (command.compareTo("0") == 0) {
            testInitClients();
            try {
                Thread.currentThread().sleep(2000);
            } catch (InterruptedException ex) {
                Logger.getLogger(ClientServerMessenger.class.getName()).log(Level.SEVERE, null, ex);
            }
            testDealCard();
        }

        if (command.compareTo("1") == 0) {
            testRequestPassLeft();
        }

        if (command.compareTo("2") == 0) {
            try {
                Thread.currentThread().sleep(2000);
            } catch (InterruptedException ex) {
                Logger.getLogger(ClientServerMessenger.class.getName()).log(Level.SEVERE, null, ex);
            }
            testSelectCardToPass_Left();
        }
    }

    private void runTest(int i) {
        if (i == 0) {
            testDealCard();
        }

        if (i == 1) {
            this.testRequestPassLeft();
        }

        if (i == 2) {
            this.testSelectCardToPass_Left();
        }

        if (i == 3) {
            this.testDoPass();
        }

        if (i == 4) {
            this.testRequestPlayCard();
            this.playLoop();
        }

        if (i == 5) {
        }
    }

    private void playLoop() {
        System.out.println("Begin play loop");
        int turn = 0, firstPlayerIndx = 0;
        int count = 0;
        boolean isFirstTurn = true;
        ArrayList<GUICard> lstPlayedCards = new ArrayList<GUICard>();

        while (count < 13) {
            if (!commandfinish) {
                continue;
            }

            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                Logger.getLogger(ClientServerMessenger.class.getName()).log(Level.SEVERE, null, ex);
            }

            System.out.println("Begin turn " + turn);

            if (played != null) {
                System.out.println("Allow Client play this card");
                this.client.lstAvailableCards.remove(played);
                this.testDoPlayCard(played.rank, played.suit, this.client.getName());
                lstPlayedCards.add(played);
                played = null;
                continue;
            }

            if (turn > 3) {
                firstPlayerIndx = KillLoser(lstPlayedCards);
                lstPlayedCards.clear();
                turn = 0;
                count++;
                continue;
            }

            if (isFirstTurn) {
                if (this.clientHas2OfClub == this.client.getName()) {
                    this.testRequestPlayerPlayCard();
                    firstPlayerIndx = this.manager.getArrayIndexOfGambler(client);
                } else {
                    Gambler cpu = this.manager.getGambler(clientHas2OfClub);
                    firstPlayerIndx = this.manager.getArrayIndexOfGambler(cpu);
                    GUICard card = this.CPU(cpu, lstPlayedCards, isFirstTurn);
                    lstPlayedCards.add(card);
                    this.testDoPlayCard(card.rank, card.CLUBS, clientHas2OfClub);
                }
                System.out.println(String.format("turn,first=%d,%d", turn, firstPlayerIndx));
                isFirstTurn = false;

            } else {
                int temp = (turn + firstPlayerIndx) % 4;
                System.out.println(String.format("temp,turn,first=%d,%d,%d", temp, turn, firstPlayerIndx));
                if (temp == this.client.getID()) {
                    this.testRequestPlayerPlayCard();
                } else {
                    Gambler cpu = this.manager.getGamblerByArrayIndex(temp);
                    GUICard card = this.CPU(cpu, lstPlayedCards, isFirstTurn);
                    lstPlayedCards.add(card);
                    this.testDoPlayCard(card.rank, card.suit, cpu.getName());
                }
            }

            turn++;
        }

        System.out.println("Finished play loop");


    }

    public ClientServerMessenger(Socket socketClient, ClientReceiver receiver, Hearts game, GameRules gameRules, JPanel pnlInfo, GUICard played, GamblerManager manager, Gambler client, String clientHas2OfClub) {
        this.socketClient = socketClient;
        this.receiver = receiver;
        this.game = game;
        this.gameRules = gameRules;
        this.pnlInfo = pnlInfo;
        this.played = played;
        this.manager = manager;
        this.client = client;
        this.clientHas2OfClub = clientHas2OfClub;
    }

    private GUICard CPU(Gambler cpu, ArrayList<GUICard> list, boolean isFirstTurn) {
        GUICard him = null;
        int firstCardId;
        int id;

        if (isFirstTurn) {
            him = cpu.findCardInAvailList(0, GUICard.CLUBS);
            cpu.lstAvailableCards.remove(him);
            id = Card.convertCardGUI_IDToCard_ID(him.toCardID());
            this.gameRules.isLeadMovingValid(id);

            return him;
        }

        ArrayList<GUICard> suitList;
        ArrayList<GUICard> applicants;

        if (list.isEmpty()) {
            suitList = cpu.findCardsInAvailListExcept(GUICard.HEARTS);
            if (suitList.isEmpty()) {
                suitList = (ArrayList<GUICard>) cpu.lstAvailableCards.clone();
            }
            applicants = suitList;
            Collections.sort(applicants);
            him = applicants.get(0);
            cpu.lstAvailableCards.remove(him);

            //hợp thức hóa thủ tục
            id = Card.convertCardGUI_IDToCard_ID(him.toCardID());
            boolean flag = this.gameRules.isLeadMovingValid(id);
            if(!flag) System.out.println("Hỏng bét");
            
            return him;
        }

        int sign = 1;

        GUICard firstCard = list.get(0);

        suitList = cpu.findCardsInAvailList(firstCard.suit);
        applicants = new ArrayList<GUICard>();

        if (suitList.isEmpty()) {
            //tìm bộ khác bất kỳ
            suitList.addAll(cpu.findCardsInAvailListExcept(firstCard.suit));
            sign = -1;
        }

        for (int i = 0; i < suitList.size(); i++) {
            GUICard card = suitList.get(i);
            if (card.compareTo(firstCard) * sign < 0) {
                applicants.add(card);
            }
        }
        
        if(applicants.isEmpty())
            applicants = suitList;

        Collections.sort(applicants);
        him = applicants.get(0);
        cpu.lstAvailableCards.remove(him);

        //hợp thức hóa thủ tục
        id = Card.convertCardGUI_IDToCard_ID(him.toCardID());
        firstCardId = Card.convertCardGUI_IDToCard_ID(firstCard.toCardID());
        this.gameRules.isMovingValid(id, firstCardId, cpu.getGamblerCards());

        return him;
    }

    private MethodResult getMethodByName(String methodname) {
        MethodResult result = new MethodResult();
        try {
            Class c = Class.forName("MiddleLayer.ClientServerMessenger");
            Method m[] = c.getDeclaredMethods();
            for (int i = 0; i < m.length; i++) {
                if (m[i].getName().compareToIgnoreCase(methodname) == 0) {
                    result.method = m[i];
                    result.obj = c.newInstance();
                    ClientServerMessenger messenger = (ClientServerMessenger) result.obj;
                    messenger.game = this.game;
                    messenger.pnlInfo = this.pnlInfo;
                    messenger.socketClient = this.socketClient;
                    return result;
                }

                System.out.println(m[i].toString());
            }
        } catch (Throwable e) {
            System.err.println(e);
        }

        return result;
    }

    private void testInitClients() {
        System.out.println("InitClients");
        commandfinish = false;

        String clientName = "so0";

        ArrayList<String> list = new ArrayList<String>(4);
        list.add("so1");
        list.add("so0");
        list.add("so2");
        list.add("so3");

        this.manager = new GamblerManager(1024, 540);
        this.manager.initPlayers(clientName, list);
        this.client = this.manager.getMainPlayer();

        this.game.initAllsClients(clientName, list);
        this.game.prepareForNextGame();
    }

    private void testDealCard() {
        System.out.println("DealCards");
        commandfinish = false;

        Deck deck = new Deck();

        deck.loadNewCards();

        String clientName = "so0";

        this.game.prepareToDeal();

        deck.shuffle();
        int j = 0;
        for (int i = 0; i < 52; i++) {
            j = j % 4;
            GUICard card = deck.lstCards.get(i);
            assert card == null;
            Gambler gambler = this.manager.getGamblerByArrayIndex(j);
            card.owner = j;
            gambler.receiveCard(card);

            if (card.isTwoOfClub()) {
                this.clientHas2OfClub = gambler.getName();
            }

            this.game.dealCardToGambler(card.rank, card.suit, gambler.getName());
            j++;

            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException ex) {
                Logger.getLogger(ClientServerMessenger.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        for (int i = 0; i < 4; i++) {
            Gambler gambler = this.manager.getGamblerByArrayIndex(i);
            gambler.sortAvailCards();
        }
    }

    private void testRequestPassLeft() {
        System.out.println("Request Pass");
        commandfinish = false;
        this.game.prepareToPass(0);//pass left
    }

    private void testSelectCardToPass_Left() {
        System.out.println("Do pass left-other client");
        commandfinish = false;

        for (int i = 1; i < 4; i++) {
            Gambler gambler = this.manager.getGamblerByArrayIndex(i);
            Cards cards = gambler.getGamblerCards();
            for (int j = 0; j < 3; j++) {
                Card c = (Card) cards.lstOfCards.get(j);
                gambler.selectCardToPass(c.getType(), c.getSuit());
                this.game.selectCardsToPass(c.getType(), c.getSuit(), gambler.getName());
            }
        }

        commandfinish = true;
    }

    private void testDoPass() {
        System.out.println("Do pass left");
        commandfinish = false;

        this.game.passSelectedCards(0);//left
        this.manager.passCards(0);
    }

    private void testRequestPlayCard() {
        System.out.println("Play Mode");
        commandfinish = false;
        this.game.prepareToPlay();
    }

    private void testRequestPlayerPlayCard() {
        System.out.println("wait client for playing card");
        commandfinish = false;
        this.game.waitClientAction();
    }

    private void testDoPlayCard(int rank, int suit, String name) {
        System.out.println(String.format("Server said Client %s play card rank=%d, suit=%d.", name, rank, suit));
        commandfinish = false;
        this.game.playCard(rank, suit, name);
    }

    private void testTakenCard(ArrayList<Integer> guiIDList, String name) {
        commandfinish = false;
        this.game.sendTakenCardsToGambler(guiIDList, name);
    }

    private int KillLoser(ArrayList<GUICard> lstPlayedCards) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            Logger.getLogger(ClientServerMessenger.class.getName()).log(Level.SEVERE, null, ex);
        }

        ArrayList<Integer> result = new ArrayList<Integer>();
        GUICard losercard = this.findLoserCard(lstPlayedCards);
        String loser = this.manager.getGamblerByArrayIndex(losercard.owner).getName();
        for (int i = 0; i < lstPlayedCards.size(); i++) {
            GUICard card = lstPlayedCards.get(i);
            int guiID = card.toCardID();
            result.add(guiID);
            System.out.println(String.format("Server said Client %s receive card rank=%d, suit=%d.", loser, card.rank, card.suit));
        }

        testTakenCard(result, loser);

        return losercard.owner;
    }

    private GUICard findLoserCard(ArrayList<GUICard> input) {
        ArrayList<GUICard> list = (ArrayList<GUICard>) input.clone();
        ArrayList<GUICard> result = new ArrayList<GUICard>();
        GUICard first = list.get(0);

        for (int i = 0; i < list.size(); i++) {
            GUICard card = list.get(i);
            if (card.suit == first.suit) {
                result.add(card);
            }
        }

        Collections.sort(result);

        return result.get(result.size() - 1);
    }
}
